<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>toDom</title>
  <style>
    #main {
      padding: 0;
      border: 1px solid black;
      width: 500px;
      height: 500px;
      position: relative;
      overflow: hidden;
    }

    #main .ball {
      background: red;
      border-radius: 50%;
      position: absolute;
      display: flex;
      align-items: center;
      justify-content: center;
      user-select: none;
      cursor: pointer;
      transition: Background 0.3s;
    }

    .ball:hover {
      font-size: 1.5rem;
      background: blue !important;
    }
  </style>
</head>

<body>
  <div id="main"></div>
  <script src="matter.js"></script>
  <script>
    const { Engine, Bodies, Composite, Events, Body, Mouse, MouseConstraint } = Matter;
    let isDragging = false;
    let dragBody = null;
    let dragOffset = { x: 0, y: 0 };
    const dome = {
      engine: null,
      world: null,
      element: null,
      init(ele) {
        this.element = ele;
        this.engine = Engine.create();
        this.world = this.engine.world;
        this.create_edge();
        this.create_ball(10);
        const mouse = Mouse.create(this.element);
        const mouseConstraint = MouseConstraint.create(this.engine, {
          mouse: mouse,
          constraint: {
            stiffness: 1,
            damping: 1,
            render: {
              visible: false,
            },
          },
        });
        Composite.add(this.world, mouseConstraint);
        Engine.run(this.engine);
      },
      create_edge() {
        const eleWidth = this.element.offsetWidth;
        const eleHeight = this.element.offsetHeight;
        const size = 100;
        const edgeList = [
          Bodies.rectangle(eleWidth / 2, -1 * size, eleWidth, size * 2, { isStatic: true }),
          Bodies.rectangle(eleWidth / 2, eleHeight + size, eleWidth, size * 2, { isStatic: true }),
          Bodies.rectangle(-1 * size, eleWidth / 2, size * 2, eleHeight, { isStatic: true }),
          Bodies.rectangle(eleWidth + size, eleHeight / 2, size * 2, eleHeight, { isStatic: true }),
        ];
        Composite.add(this.world, edgeList);
      },
      create_ball(num) {
        for (let i = 0; i < num; i++) {
          const ball = document.createElement('div');
          ball.className = "ball";
          ball.innerHTML = i;
          const r = Math.random() * 100 + 50;
          ball.style.width = r + "px";
          ball.style.height = r + "px";
          ball.style.left = Math.random() * (this.element.offsetWidth - r / 2) + "px";
          ball.style.top = Math.random() * (this.element.offsetHeight - r / 2) + "px";
          this.element.appendChild(ball);
          this.MatterBody(ball);
        }
      },
      MatterBody(ball) {
        const r = ball.offsetWidth / 2;
        const ballBody = Bodies.circle(
          ball.offsetLeft + r,
          ball.offsetTop + r,
          r,
          {
            restitution: 0.8, // 弹性
            friction: 0.01,
            frictionAir: 0.01,
          }
        );
        Composite.add(this.world, ballBody);
        ball.addEventListener('mousedown', (event) => this.onMouseDown(event, ball, ballBody));
        document.addEventListener('mousemove', (event) => this.onMouseMove(event));
        document.addEventListener('mouseup', () => this.onMouseUp());
        const angleInDegrees = ballBody.angle * (180 / Math.PI);
        Events.on(this.engine, 'afterUpdate', () => {
          requestAnimationFrame(() => {
            ball.style.transform = `rotate(${angleInDegrees}deg)`;
            ball.style.left = `${ballBody.position.x - r}px`;
            ball.style.top = `${ballBody.position.y - r}px`;
          })
        });
      },
      onMouseDown(event, ball, ballBody) {
        isDragging = true;
        dragBody = ballBody;
        const rect = ball.getBoundingClientRect();
        const offsetX = event.clientX - rect.left;
        const offsetY = event.clientY - rect.top;
        dragOffset = { x: offsetX - rect.width / 2, y: offsetY - rect.width / 2 };
      },
      onMouseMove(event) {
        if (isDragging && dragBody) {
          let mousePos = {
            x: event.clientX - main.offsetLeft, // 考虑页面滚动
            y: event.clientY - main.offsetTop,
          };
          const mainRect = main.getBoundingClientRect();
          if (
            mousePos.x < 0 ||
            mousePos.x > mainRect.width ||
            mousePos.y < 0 ||
            mousePos.y > mainRect.height
          ) {
            // 鼠标移出main容器，停止拖拽
            isDragging = false;
            dragBody = null;
            console.log("Dragging canceled: Mouse moved out of main container.");
            return;
          }
          Body.setPosition(dragBody, {
            x: mousePos.x + dragOffset.x,
            y: mousePos.y + dragOffset.y,
          });
        }
      },

      onMouseUp() {
        isDragging = false;
        dragBody = null;
      },
    };
    const main = document.querySelector('#main');
    dome.init(main);
  </script>
</body>

</html>